// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <functional>
#include "CaeControlCmdHandleThread.h"
#include "../cae_service/CaeSocketAgent.h"
#include "../cae_common/CaeAppCtrlCmdUtils.h"
#include "../cae_common/CaeVirtualDeviceConfig.h"
#include "../cae_agent/CaeAgent.h"
#include "../cae_common/CaeMsgCode.h"
#include "../cae_common/CaeCommon.h"
#include "../cae_service/CaeHttpsJob.h"
#include "../cae_common/CaeJsonUtil.h"
#include "../cae_service/CaeMediaConfig.h"

namespace {
const int DEFAULT_WIDTH = 720;
const int DEFAULT_HEIGHT = 1280;
const int DEFAULT_BITRATE = 3000000;
const int DEFAULT_FRAME_RATE = 30;
const int DEFAULT_ENCODE_TYPE = 0;
const char* DEFAULT_FRAME_TYPE = "h264";
const int MILLISECONDS_PER_MINUTE = 60000;
}

CaeControlCmdHandleThread::CaeControlCmdHandleThread(const std::weak_ptr<CaeSocketAgent>& socketAgent)
{
    this->m_threadStatus = CAE_THREAD_STATUS_NEW;
    this->m_aesInfo = nullptr;
    this->m_socketAgent = socketAgent;
}

CaeControlCmdHandleThread::~CaeControlCmdHandleThread()
{
    m_threadStatus = CAE_THREAD_STATUS_EXIT;
    Deinit();
}

bool IsScreenSleep()
{
    char interactiveState[1024];
    memset_s(interactiveState, sizeof(interactiveState), '\0', sizeof(interactiveState));
    std::string dumpsysCurrentWindow = "dumpsys window policy | grep interactiveState | grep INTERACTIVE_STATE_SLEEP";
    FILE *fp = popen(dumpsysCurrentWindow.c_str(), "r");
    if (fgets(interactiveState, sizeof(interactiveState) - 1, fp) == nullptr) {
        LOGI("no need to unlock the screen");
        pclose(fp);
        fp = nullptr;
        return false;
    }
    pclose(fp);
    fp = nullptr;
    return true;
}

void CaeControlCmdHandleThread::HandleStartCmd(std::map<std::string, std::string> &parameters)
{
    CaeAgent &caeAgentInstance = CaeAgent::GetInstance();
    if (parameters.empty()) {
        return;
    }
    LOGI("Receive start control cmd.");

    if (!m_socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
        if (socketAgent->GetSocketAgentState() == CAE::DEINIT) {
            LOGI("Client has been marked for deinit, do nothing.");
            return;
        }
    }

    if (!CheckAndStoreParams(parameters)) {
        std::map<std::string, std::string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_VERIFY_PARAMETER_MISSING);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, m_socketAgent)) {
            LOGE("Failed to send the CAE_VERIFY_PARAMETER_MISSING parameter.");
            caeAgentInstance.CloseClient();
        }
        return;
    }

    bool secondVerify = false;
    if (!m_socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
        LOGI("Socket agent state:%s.", socketAgent->GetSocketAgentStateString().c_str());
        secondVerify = socketAgent->GetSocketAgentState() == CAE::VERIFYED;
        socketAgent->SetSocketAgentState(CAE::VERIFYING);
    }

    int queryAesKeyRetParam = QueryAesKeyAndVerifyData(parameters);
    if (queryAesKeyRetParam != CAE_VERIFY_SUCCESS) {
        std::map<std::string, std::string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(queryAesKeyRetParam);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, m_socketAgent)) {
            LOGE("Failed to send the queryAesKeyRetParam.");
            caeAgentInstance.CloseClient();
        }
        if (m_verifyRetCallback != nullptr) {
            if (!m_socketAgent.expired()) {
                std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
                m_verifyRetCallback(false, secondVerify, socketAgent->GetSocketConnectionId(),
                                    m_paramStorage->GetClientPlatformType());
            }
        }
        return;
    }

    if (parameters.find(KEY_MEDIA_CONFIG) == parameters.end()) {
        LOGW("KEY_MEDIA_CONFIG not found.");
    } else {
        ParseMediaConfig(parameters[KEY_MEDIA_CONFIG]);
    }
    if (!m_socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
        m_verifyRetCallback(true, secondVerify, socketAgent->GetSocketConnectionId(),
                            m_paramStorage->GetClientPlatformType());
    }

    // AOSP11判断是否锁屏
    std::string sdkVersion = CaeConfigManage::GetSDKVersion();
    try {
        if (!sdkVersion.empty() && stoi(sdkVersion) >= ANDROID_11_SDK_VERSION && IsScreenSleep()) {
            LOGI("Need to unlock screen.");
            std::system("input keyevent 26");
        }
    } catch (const std::invalid_argument& e) {
        LOGE("Failed to get sdkVersion");
    } catch (const std::out_of_range& e) {
        LOGE("sdkVersion is out of range.");
    }

    if (CaeConfigManage::EnableEventNotify()) {
        time_t startAppTimestamp = time(nullptr);
        m_paramStorage->SetStartTime(startAppTimestamp);

        int result = caeAgentInstance.m_caeHttpsJobThread->AddHttpsJob(HttpsJobType::NOTIFY_START,
                                                                       m_paramStorage, m_socketAgent);
        if (result < 0) {
            LOGE("Failed to add HttpsJob.");
            caeAgentInstance.CloseClient();
            return;
        }
    }
}

bool CaeControlCmdHandleThread::CheckAndStoreParams(map<std::string, std::string> &parameters)
{
    if (parameters.find(KEY_TICKET) == parameters.end()) {
        LOGE("KEY_TICKET not found.");
        return false;
    }
    m_paramStorage->SetTicket(parameters[KEY_TICKET]);

    if (parameters.find(KEY_SESSION_ID) == parameters.end()) {
        LOGE("SESSION_ID not found.");
        return false;
    }
    m_paramStorage->SetSessionId(parameters[KEY_SESSION_ID]);


    if (parameters.find(KEY_AUTH_TS) == parameters.end()) {
        LOGE("KEY_AUTH_TS not found.");
        return false;
    }
    m_paramStorage->SetAuthTs(parameters[KEY_AUTH_TS]);


    if (parameters.find(KEY_VERIFY_DATA) == parameters.end()) {
        LOGE("KEY_VERIFY_DATA not found.");
        return false;
    }
    m_paramStorage->SetVerifyData(parameters[KEY_VERIFY_DATA]);

    if (parameters.find(KEY_ENCRYPTED_DATA) == parameters.end()) {
        LOGE("KEY_ENCRYPTED_DATA not found.");
        return false;
    }
    m_paramStorage->SetEncyptedData(parameters[KEY_ENCRYPTED_DATA]);


    if (parameters.find(KEY_AES_IV) == parameters.end()) {
        LOGE("KEY_AES_IV not found.");
        return false;
    }
    m_paramStorage->SetAesIv(parameters[KEY_AES_IV]);

    if (parameters.find(KEY_SDK_VERSION) == parameters.end()) {
        LOGE("KEY_SDK_VERSION not found.");
        return false;
    }
    m_paramStorage->SetSdkVersion(parameters[KEY_SDK_VERSION]);
    LOGI("Client version is %s", m_paramStorage->GetSdkVersion().c_str());

    if (parameters.find(KEY_PROTOCOL_VERSION) == parameters.end()) {
        LOGE("KEY_PROTOCOL_VERSION not found.");
        return false;
    }
    m_paramStorage->SetProtolcolVersion(parameters[KEY_PROTOCOL_VERSION]);

    if (parameters.find(KEY_CLIENT_MODE) == parameters.end() || parameters[KEY_CLIENT_MODE].empty()) {
        if (CaeCommon::CompareVersion(m_paramStorage->GetSdkVersion(), CLIENT_KEY_VERSION) >= 0) {
            LOGE("KEY_CLIENT_MODE not found.");
            return false;
        } else {
            m_paramStorage->SetClientMode(CLIENT_MODE_MANAGEMENT);
        }
    } else {
        if (parameters[KEY_CLIENT_MODE] != CLIENT_MODE_MANAGEMENT && parameters[KEY_CLIENT_MODE] != CLIENT_MODE_DIRECT) {
            LOGE("KEY_CLIENT_MODE is invalid.");
            return false;
        }
        m_paramStorage->SetClientMode(parameters[KEY_CLIENT_MODE]);
    }

    if (parameters.find(KEY_CLIENT_TYPE) == parameters.end()) {
        LOGW("KEY_CLIENT_TYPE not found.");
    }
    m_paramStorage->SetClientPlatformType(parameters[KEY_CLIENT_TYPE]);

    if (parameters.find(KEY_REGION_ID) == parameters.end()) {
        LOGW("KEY_REGION_ID not found.");
    }
    m_paramStorage->SetRegionId(parameters[KEY_REGION_ID]);

    if (parameters.find(KEY_MAX_DISCONNECT_DURATION) != parameters.end()) {
        m_paramStorage->SetMaxDisconnectDuration(
            parameters[KEY_MAX_DISCONNECT_DURATION]);
        LOGI("KEY_MAX_DISCONNECT_DURATION is %s.", parameters[KEY_MAX_DISCONNECT_DURATION].c_str());
    } else {
        LOGW("KEY_MAX_DISCONNECT_DURATION not found.");
        std::string timeString = "0";
        m_paramStorage->SetMaxDisconnectDuration(timeString);
    }

    // 增加串流或者截图参数
    if (parameters.find(KEY_OPERATE_TYPE) == parameters.end()) {
        LOGW("KEY_OPERATE_TYPE not found.");
        m_paramStorage->SetIsScreenshot("");
    } else {
        m_paramStorage->SetIsScreenshot(parameters[KEY_OPERATE_TYPE]);
    }

    return true;
}

bool CaeControlCmdHandleThread::UpdateCertFile()
{
    long now = CaeCommon::GetCurrentTimestamp();
    long last = CaeAgent::GetInstance().GetLastRequestPublicKeyTimestamp();
    if(now - last < MILLISECONDS_PER_MINUTE) {
        return true;
    }
    CaeHttpsJob &caeHttpsJob = CaeHttpsJob::GetInstance();
    caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_PUBLIC_CRT);
    CaeAgent::GetInstance().m_caeHttpsJobThread->AddHttpsJob(HttpsJobType::QUERY_PUBLIC_CRT, m_paramStorage, m_socketAgent);
    int index = 0;
    if (!caeHttpsJob.ContainResponse(HttpsJobType::QUERY_PUBLIC_CRT)) {
        return false;
    }
    HttpsResponse &responseQueryPublicKey = caeHttpsJob.GetResponse(HttpsJobType::QUERY_PUBLIC_CRT);
    do {
        if (responseQueryPublicKey.size > 0) {
            LOGI("Receive queryPublicKey response.");
            break;
        }
        usleep(50000);
        index++;
    } while (index < 200);

    if (responseQueryPublicKey.size <= 0) {
        LOGE("Failed to get queryPublicKey response, queryIndex is %d", index);
        return false;
    }
    if (!caeHttpsJob.ParseHttpsResponse(responseQueryPublicKey, HttpsJobType::QUERY_PUBLIC_CRT)) {
        caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_PUBLIC_CRT);
        return false;
    }
    caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_PUBLIC_CRT);
    CaeAgent::GetInstance().SetLastRequestPublicKeyTimestamp(CaeCommon::GetCurrentTimestamp());
    return true;
}

int CaeControlCmdHandleThread::VerifySignatureWithPublicKeyCrt(string phoneId)
{
    string filePath = m_paramStorage->GetPublicCrtFilePath();
    if (filePath.empty()) {
        LOGE("Public crt file path is invalid.");
        return CAE_VERIFY_FAILED;
    }

    if (!CaeCommon::IsFileExist(filePath)) {
        CaeAgent::GetInstance().SetLastRequestPublicKeyTimestamp(0);
        if (!UpdateCertFile()) {
            LOGE("Failed to get public key from network");
            return CAE_VERIFY_FAILED;
        }
    }

    string data = phoneId + m_paramStorage->GetSessionId() + m_paramStorage->GetAuthTs();
    string ticket = m_paramStorage->GetTicket();
    if (!CaeSignUtil::VerifySignature(CaeCommon::ReadFileToString(filePath), data, ticket)) {
        LOGE("Verify signature failed, request public key and retry.");
        if (!UpdateCertFile()) {
            LOGE("Failed to get public key from network");
            return CAE_VERIFY_FAILED;
        }
        if (!CaeSignUtil::VerifySignature(CaeCommon::ReadFileToString(filePath), data, ticket)) {
            LOGE("Verify signature failed");
            return CAE_VERIFY_PARAMETER_INVALID;
        }
    }
    LOGI("Verify signature success");
    return CAE_VERIFY_SUCCESS;
}

string CaeControlCmdHandleThread::QueryAesKeyWithSignCheckUrl()
{
    CaeHttpsJob &caeHttpsJob = CaeHttpsJob::GetInstance();
    caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
    CaeAgent::GetInstance().m_caeHttpsJobThread->AddHttpsJob(HttpsJobType::QUERY_AES_KEY, m_paramStorage, m_socketAgent);
    int index = 0;
    if (!caeHttpsJob.ContainResponse(HttpsJobType::QUERY_AES_KEY)) {
        return "";
    }
    HttpsResponse &responseQueryAesKey = caeHttpsJob.GetResponse(HttpsJobType::QUERY_AES_KEY);
    do {
        if (responseQueryAesKey.size > 0) {
            LOGI("Receive queryAesKey response.");
            break;
        }
        usleep(50000);
        index++;
    } while (index < 200);
    if (responseQueryAesKey.size <= 0) {
        LOGE("Failed to get queryAesKey response, queryIndex is %d", index);
        return "";
    }
    if (!caeHttpsJob.ParseHttpsResponse(responseQueryAesKey, HttpsJobType::QUERY_AES_KEY)) {
        caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
        return "";
    }
    string responseStr;
    responseStr = string(responseQueryAesKey.buffer, responseQueryAesKey.size);
    cJSON *root = cJSON_Parse(responseStr.c_str());
    if (!root) {
        LOGE("JSON Error before: %s.", cJSON_GetErrorPtr());
        caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
        return "";
    }
    string aesKey = CaeJsonUtil::GetJSONString(root, "aes_key");
    cJSON_Delete(root);
    caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
    return aesKey;
}

int CaeControlCmdHandleThread::QueryAesKeyAndVerifyData(map<string, string> &parameters)
{
    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_VERIFYING, m_socketAgent);

    std::string aesKey = CaeConfigManage::GetPhoneId();
    bool isNewVersion = CaeCommon::CompareVersion(m_paramStorage->GetSdkVersion(), CLIENT_KEY_VERSION) >= 0;
    if (isNewVersion) {
        long auTs;
        try {
            auTs = stol(m_paramStorage->GetAuthTs());
            if (CaeCommon::GetCurrentTimestamp() - auTs > CaeConfigManage::GetTicketExpirationTime()) {
                LOGE("auth timestamp is expired.");
                return CAE_VERIFY_PARAMETER_INVALID;
            }
        } catch (const std::invalid_argument& e) {
            LOGE("auth timestamp is expired.");
            return CAE_VERIFY_PARAMETER_INVALID;
        } catch (const std::out_of_range& e) {
            LOGE("sdkVersion is out of range.");
            return CAE_VERIFY_PARAMETER_INVALID;
        }
    }

    if (m_paramStorage->GetClientMode() == CLIENT_MODE_MANAGEMENT) {
        if (!CaeConfigManage::GetManagementPublicCrtObtainUrl().empty() && isNewVersion) {
            int ret = VerifySignatureWithPublicKeyCrt(aesKey);
            if (ret != CAE_VERIFY_SUCCESS) {
                return ret;
            }
        } else if (CaeConfigManage::EnableSignCheck()) {
            aesKey = QueryAesKeyWithSignCheckUrl();
            if (aesKey.empty()) {
                LOGE("Failed to get aes key from SignCheckUrl.");
                return CAE_VERIFY_PARAMETER_INVALID;
            }
        }
    } else {
        int ret = VerifySignatureWithPublicKeyCrt(aesKey);
        if (ret != CAE_VERIFY_SUCCESS) {
            return ret;
        }
    }

    if (aesKey.empty()) {
        LOGE("AesKey is null.");
        return CAE_VERIFY_AES_KEY_INVALID;
    }
    m_paramStorage->SetAesKey(aesKey);
    int ret = memset_s(m_aesInfo, sizeof(AesInfo), 0, sizeof(AesInfo));
    if (ret != EOK) {
        LOGE("Failed to memset.");
        return CAE_VERIFY_AES_KEY_INVALID;
    }
    m_aesInfo->keyType = KEY_AES_128;
    m_aesInfo->tagLen = 16;
    m_aesInfo->strKey = aesKey;
    m_aesInfo->strIv = m_paramStorage->GetAesIv();

    if (!ValidAesInfo(m_aesInfo)) {
        memset_s(m_aesInfo, sizeof(AesInfo), 0, sizeof(AesInfo));
        LOGE("AesInfo invalid");
        return CAE_VERIFY_AES_KEY_INVALID;
    }
    string encryptedData = m_paramStorage->GetEncyptedData();
    if (!ParseStartInfo(encryptedData, m_aesInfo)) {
        return CAE_VERIFY_DECRYPT_FAILED;
    }

    return CAE_VERIFY_SUCCESS;
}

bool CaeControlCmdHandleThread::ParseStartInfo(string &encryptData, AesInfo *aesInfo)
{
    string decryptData;
    if (!CaeAesGcmDecrypt(encryptData, aesInfo, decryptData)) {
        LOGE("Failed to decrypt CaeAesGcm.");
        return false;
    }

    cJSON *root = cJSON_Parse(decryptData.c_str());
    if (!root) {
        LOGE("Json error before: %s.", cJSON_GetErrorPtr());
        return false;
    }

    string ip = CaeJsonUtil::GetJSONString(root, KEY_IP.data());
    m_paramStorage->SetIp(ip);

    string port = CaeJsonUtil::GetJSONString(root, KEY_PORT.data());
    m_paramStorage->SetPort(port);

    string sessionId = CaeJsonUtil::GetJSONString(root, KEY_SESSION_ID.data());
    if (sessionId.empty()) {
        LOGE("SessionId is invalid!");
        cJSON_Delete(root);
        return false;
    }
    m_paramStorage->SetSessionId(sessionId);

    string touchTimeout = CaeJsonUtil::GetJSONString(root, KEY_TOUCH_TIMEOUT.data());
    LOGD("Touch timeout is %s.", touchTimeout.c_str());
    if (touchTimeout.empty()) {
        touchTimeout = "0";
    }
    m_paramStorage->SetTouchTimeout(touchTimeout);

    string backgroundTimeout = CaeJsonUtil::GetJSONString(root, KEY_BACKGROUND_TIMEOUT.data());
    m_paramStorage->SetBackgroundTimeout(backgroundTimeout);

    string availablePlayTime = CaeJsonUtil::GetJSONString(root, KEY_AVAILABLE_PLAYTIME.data());
    m_paramStorage->SetAvailablePlayTime(availablePlayTime);

    string tmpVerifyStr;
    string userId = CaeJsonUtil::GetJSONString(root, KEY_USER_ID.data());
    m_paramStorage->SetUserId(userId);
    tmpVerifyStr = ip + port + sessionId + backgroundTimeout + availablePlayTime + userId;

    string encodedStr;
    if (!CaeSignUtil::CAESha256(tmpVerifyStr, encodedStr)) {
        LOGE("Failed to get sha256 of tmpVerifyStr.");
        cJSON_Delete(root);
        return false;
    }

    if (encodedStr != m_paramStorage->GetVerifyData()) {
        LOGE("Failed to verify encodedStr.");
        cJSON_Delete(root);
        return false;
    }

    if (!availablePlayTime.length()) {
        LOGE("AvailablePlayTime:%s is invalid", availablePlayTime.c_str());
        cJSON_Delete(root);
        return false;
    }
    LOGI("ParseStartInfo Success.");
    cJSON_Delete(root);
    return true;
}

void CaeControlCmdHandleThread::HandleSetMediaConfigCmd(map<std::string, std::string> &parameters)
{
    if (parameters.empty()) {
        LOGW("Parameters is empty");
        return;
    }
    if (parameters.find(KEY_MEDIA_CONFIG) == parameters.end()) {
        LOGW("KEY_MEDIA_CONFIG not found.");
        return;
    }
    bool oldMuteFlag, newMuteFlag;
    if (parameters[KEY_MEDIA_CONFIG].empty()) {
        LOGW("Media config is empty");
        return;
    } else {
        oldMuteFlag = CaeMediaConfig::GetInstance().IsMute();
        ParseMediaConfig(parameters[KEY_MEDIA_CONFIG]);
        newMuteFlag = CaeMediaConfig::GetInstance().IsMute();
    }

    if (oldMuteFlag != newMuteFlag) {
        if (newMuteFlag) {
            CaeEngineControl::GetInstance().CloseAudio();
        } else {
            CaeEngineControl::GetInstance().OpenAudio();
        }
    }

    bool ret = false;
    Result setMediaConfigRet = CaeEngineControl::GetInstance().SetMediaConfig();
    if (setMediaConfigRet != Result::SUCCESS) {
        LOGE("Failed to set media config.");
        ret = CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_SET_MEDIA_CONFIG_ERROR, m_socketAgent);
        if (!ret) {
            LOGE("Failed to send CAE_SET_MEDIA_CONFIG_ERROR message.");
        }
    } else {
        ret = CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_SET_MEDIA_CONFIG_SUCCESS, m_socketAgent);
        if (!ret) {
            LOGE("Send CAE_SET_MEDIA_CONFIG_SUCCESS message success.");
        }
    }
}

void CaeControlCmdHandleThread::HandleStopCmd(map<string, string> &parameters)
{
    if (m_actionCallback == nullptr) {
        return;
    }

    if (m_socketAgent.expired()) {
        return;
    }

    std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();

    if (CaeConfigManage::EnableEventNotify() && socketAgent->IsVerifyComplete()) {
        int res = CaeAgent::GetInstance().m_caeHttpsJobThread->AddHttpsJob(HttpsJobType::NOTIFY_STOP, m_paramStorage, socketAgent);
        if (res < 0) {
            LOGE("Failed to add job for notify stop.");
        }
    }

    socketAgent->DisableSend();
    string connectionId = socketAgent->GetSocketConnectionId();
    m_actionCallback(connectionId, COMMAND_TYPE::CMD_STOP);
}

void CaeControlCmdHandleThread::HandlePauseCmd(std::map<std::string, std::string> &parameters)
{
    if (m_actionCallback == nullptr) {
        return;
    }

    if (!m_socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
        string connectionId = socketAgent->GetSocketConnectionId();
        m_actionCallback(connectionId, COMMAND_TYPE::CMD_PAUSE);
    }
}

void CaeControlCmdHandleThread::HandleReconnectCmd(map<string, string> &parameters)
{
    string tempSessionId;
    if (parameters.find(KEY_SESSION_ID) == parameters.end()) {
        LOGE("SESSION_ID not found.");
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_RECONNECT_PARAMETER_INVALID);
        if (!CaeClientNotifyTool::GetInstance().ResponseCmdToClient(params, m_socketAgent)) {
            LOGE("Failed to send CAE_RECONNECT_PARAMETER_INVALID param to client failed");
        }
        return;
    } else {
        tempSessionId = parameters[KEY_SESSION_ID];
        m_paramStorage->SetSessionId(tempSessionId);
    }

    LOGI("Reconnect sessionId: %s", tempSessionId.c_str());
    if (m_actionCallback != nullptr) {
        if (!m_socketAgent.expired()) {
            std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
            string connectionId = socketAgent->GetSocketConnectionId();
            m_actionCallback(connectionId, COMMAND_TYPE::CMD_RECONNECT);
        }
    }
}

void CaeControlCmdHandleThread::HandleResumeCmd(map<string, string> &parameters)
{
    if (m_actionCallback == nullptr) {
        return;
    }

    if (!m_socketAgent.expired()) {
        std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
        string connectionId = socketAgent->GetSocketConnectionId();
        m_actionCallback(connectionId, COMMAND_TYPE::CMD_RESUME);
    }
}

char* CaeControlCmdHandleThread::ParseMediaConfig(std::string mediaConfig)
{
    LOGI("Begin to parse media config %s", mediaConfig.c_str());
    if (mediaConfig.empty()) {
        LOGD("Media config is empty");
        return "";
    }

    map<std::string, std::string> mediaConfigMap =
        CaeAppCtrlCmdUtils::ParseCommand(mediaConfig.c_str(), mediaConfig.size(), SUB_COMMAND_SEPARATOR);
    bool isScreenshot = m_paramStorage->IsScreenshot();
    if (isScreenshot) {
        if (mediaConfigMap.find(KEY_STREAM_WIDTH) == mediaConfigMap.end()) {
            LOGD("MediaConfig: KEY_STREAM_WIDTH not found.");
        } else {
            m_paramStorage->SetJpegWidth(mediaConfigMap[KEY_STREAM_WIDTH].c_str());
        }

        if (mediaConfigMap.find(KEY_STREAM_HEIGHT) == mediaConfigMap.end()) {
            LOGD("MediaConfig: KEY_STREAM_HEIGHT not found.");
        } else {
            m_paramStorage->SetJpegHeight(mediaConfigMap[KEY_STREAM_HEIGHT].c_str());
        }
        return "";
    }

    if (mediaConfigMap.find(KEY_FRAME_TYPE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_FRAME_TYPE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetVideoFrameType(mediaConfigMap[KEY_FRAME_TYPE].c_str());
    }

    if (mediaConfigMap.find(KEY_QUALITY) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_QUALITY not found.");
    } else {
        CaeMediaConfig::GetInstance().SetQuality(mediaConfigMap[KEY_QUALITY].c_str());
    }

    if (mediaConfigMap.find(KEY_AUTO_QUALITY) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_AUTO_QUALITY not found.");
    } else {
        CaeMediaConfig::GetInstance().SetAutoQuality(mediaConfigMap[KEY_AUTO_QUALITY].c_str());
    }

    if (mediaConfigMap.find(KEY_FRAME_RATE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_FRAME_RATE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetFrameRate(mediaConfigMap[KEY_FRAME_RATE].c_str());
    }

    if (mediaConfigMap.find(KEY_BITRATE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_BITRATE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetBitrate(mediaConfigMap[KEY_BITRATE].c_str(), true);
    }

    if (mediaConfigMap.find(KEY_ENCODE_TYPE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_ENCODE_TYPE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetEncodeType(mediaConfigMap[KEY_ENCODE_TYPE].c_str());
    }

    if (mediaConfigMap.find(KEY_REMOTE_SCHEDULING_ELB_IP) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_REMOTE_SCHEDULING_ELB_IP not found.");
    } else {
        CaeMediaConfig::GetInstance().SetRemoteSchedulingElbIp(mediaConfigMap[KEY_REMOTE_SCHEDULING_ELB_IP].c_str());
    }

    if (mediaConfigMap.find(KEY_REMOTE_SCHEDULING_ELB_PORT) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_REMOTE_SCHEDULING_ELB_PORT not found.");
    } else {
        CaeMediaConfig::GetInstance().SetRemoteSchedulingElbPort(mediaConfigMap[KEY_REMOTE_SCHEDULING_ELB_PORT].c_str());
    }

    if (mediaConfigMap.find(KEY_REPEAT_FPS) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_REPEAT_FPS not found.");
    } else {
        CaeMediaConfig::GetInstance().SetRepeatFps(mediaConfigMap[KEY_REPEAT_FPS].c_str());
    }

    if (mediaConfigMap.find(KEY_MUTE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_MUTE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetMute(mediaConfigMap[KEY_MUTE].c_str());
    }

    if (mediaConfigMap.find(KEY_PHYSICAL_WIDTH) == mediaConfigMap.end() ||
        mediaConfigMap.find(KEY_PHYSICAL_HEIGHT) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_PHYSICAL_WIDTH or KEY_PHYSICAL_HEIGHT not found.");
    } else {
        CaeMediaConfig::GetInstance().SetNeedSetPhysicalResolution(true);
        uint32_t physicalWidth = CaeConfigManage::GetHardwareWidth();
        uint32_t physicalHeight = CaeConfigManage::GetHardwareHeight();
        uint32_t width = CaeCommon::StrToUInt32(KEY_PHYSICAL_WIDTH.c_str(),
                                                mediaConfigMap[KEY_PHYSICAL_WIDTH].c_str(),
                                                physicalWidth);
        uint32_t height = CaeCommon::StrToUInt32(KEY_PHYSICAL_HEIGHT.c_str(),
                                                 mediaConfigMap[KEY_PHYSICAL_HEIGHT].c_str(),
                                                 physicalHeight);
        if (width != 0 && height != 0) {
            if (((float)physicalWidth / float(physicalHeight)) >= ((float)width / (float)height)) {
                physicalWidth = static_cast<uint32_t>(physicalHeight * width / height);
            } else {
                physicalHeight = static_cast<uint32_t>(physicalWidth * height / width);
            }
            CaeMediaConfig::GetInstance().SetPhysicalHeight(physicalHeight);
            CaeMediaConfig::GetInstance().SetPhysicalWidth(physicalWidth);
        } else {
            LOGE("Invalid physical size, width is [%d], height is [%d]", width, height);
        }
    }

    if (mediaConfigMap.find(KEY_STREAM_WIDTH) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_STREAM_WIDTH not found.");
    } else {
        CaeMediaConfig::GetInstance().SetStreamWidth(mediaConfigMap[KEY_STREAM_WIDTH].c_str());
    }

    if (mediaConfigMap.find(KEY_STREAM_HEIGHT) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_STREAM_HEIGHT not found.");
    } else {
        CaeMediaConfig::GetInstance().SetStreamHeight(mediaConfigMap[KEY_STREAM_HEIGHT].c_str());
    }

    cJSON *mediaConfigJson = cJSON_CreateObject();
    for (auto it = mediaConfigMap.begin(); it != mediaConfigMap.end(); ++it) {
        const char* key = it->first.c_str();
        if (key == KEY_FRAME_TYPE || key == KEY_REMOTE_SCHEDULING_ELB_IP) {
            cJSON_AddItemToObject(mediaConfigJson, key, cJSON_CreateString(it->second.c_str()));
        } else if (key == KEY_MUTE || key == KEY_QUALITY) {
            continue;
        } else {
            cJSON_AddItemToObject(mediaConfigJson, key, cJSON_CreateNumber(
                    CaeCommon::StrToUInt32(key, it->second.c_str(), 0)
                    ));
        }
    }
    char *mediaConfigStr = cJSON_Print(mediaConfigJson);
    cJSON_Delete(mediaConfigJson);

    LOGI("End to parse media config");
    return mediaConfigStr;
}

bool CaeControlCmdHandleThread::Init()
{
    m_aesInfo = new (std::nothrow) AesInfo();
    if (m_aesInfo == nullptr) {
        LOGE("Param m_aesInfo is null.");
        return false;
    }
    int ret = memset_s(m_aesInfo, sizeof(AesInfo), 0, sizeof(AesInfo));
    if (ret != EOK) {
        LOGE("Failed to memset m_aesInfo.");
        return false;
    }

    m_paramStorage = std::make_shared<CaeParamStorage>();
    return true;
}

void CaeControlCmdHandleThread::Deinit() noexcept
{
    if (this->m_aesInfo != nullptr) {
        delete this->m_aesInfo;
        this->m_aesInfo = nullptr;
    }
    m_paramStorage = nullptr;
    m_verifyRetCallback = nullptr;
    m_actionCallback = nullptr;
}

void CaeControlCmdHandleThread::HandleCommand(int commandType, map<std::string, std::string> parameters)
{
    switch (commandType) {
        case COMMAND_TYPE::CMD_START: {
            CaeCommon::PrintfCurrentTime("Command type is CMD_START");
            HandleStartCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_STOP: {
            LOGI("Command type is CMD_STOP");
            HandleStopCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_SET_MEDIA_CONFIG: {
            LOGI("Command type is CMD_SET_MEDIA_CONFIG");
            HandleSetMediaConfigCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_RECONNECT: {
            LOGI("Command type is CMD_RECONNECT");
            HandleReconnectCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_PAUSE: {
            LOGI("Command type is CMD_PAUSE");
            HandlePauseCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_RESUME: {
            LOGI("Command type is CMD_RESUME");
            HandleResumeCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_REQ_IFRAME: {
            LOGI("Command type is CMD_REQ_IFRAME");
            CaeEngineControl::GetInstance().RequestIframe();
            break;
        }
        default: {
            LOGE("Unknown cmd type.");
            break;
        }
    }
}

void CaeControlCmdHandleThread::Run()
{
    char threadName[] = "CtrlThread";
    prctl(PR_SET_NAME, threadName);
    LOGI("CaeControlCmdHandleThread RUN.");
    if (!Init()) {
        LOGE("Init failed");
        Deinit();
        return;
    }

    while (m_threadStatus == CAE_THREAD_STATUS_RUNNING) {
        CaePipeData *pipeData = nullptr;
        if (!m_socketAgent.expired()) {
            std::shared_ptr<CaeSocketAgent> socketAgent = m_socketAgent.lock();
            pipeData = socketAgent->GetCmdPkt();
        }
        if (pipeData != nullptr && pipeData->GetLen() > 0) {
            const char *receivedMsgString = (const char *)(pipeData->GetBuffer());
            map<string, string> parameters = CaeAppCtrlCmdUtils::ParseCommand(receivedMsgString,
                                                                              pipeData->GetLen());
            delete pipeData;
            pipeData = nullptr;

            if (parameters.find(KEY_COMMAND) == parameters.end()) {
                LOGE("KEY_COMMAND not found.");
                continue;
            }
            int commandType = atoi((parameters[KEY_COMMAND]).c_str());
            LOGI("CommandType is %d", commandType);
            HandleCommand(commandType, parameters);
        } else {
            if (pipeData != nullptr) {
                delete pipeData;
                pipeData = nullptr;
            }
        }
        usleep(10);
    }
    Deinit();
    m_threadStatus = CAE_THREAD_STATUS_EXIT;
    LOGI("CaeControlCmdHandleThread exit.");
}

const shared_ptr<CaeParamStorage> &CaeControlCmdHandleThread::GetParamStorage() const
{
    return m_paramStorage;
}

void CaeControlCmdHandleThread::SetParamStorage(shared_ptr<CaeParamStorage> &paramStorage)
{
    m_paramStorage = paramStorage;
}

void CaeControlCmdHandleThread::SetVerifyCallback(const VerifyRetCallback & callback)
{
    m_verifyRetCallback = callback;
}

void CaeControlCmdHandleThread::SetActionCallback(const ActionCallback &callback)
{
    m_actionCallback = callback;
}